package cn.lhn.ridersclub.service;

import cn.lhn.ridersclub.dto.AccountDto;
import cn.lhn.ridersclub.dto.ChatMessageDto;
import cn.lhn.ridersclub.dto.MsgListDto;
import cn.lhn.ridersclub.entity.BlackList;
import cn.lhn.ridersclub.entity.Message;
import cn.lhn.ridersclub.mapper.AccountMapper;
import cn.lhn.ridersclub.mapper.BlacklistMapper;
import cn.lhn.ridersclub.mapper.MessageMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class MessageServiceImpl implements MessageService {

    private final MessageMapper messageMapper;

    private final AccountMapper accountMapper;

    private final AccountService accountService;

    private final BlacklistMapper blacklistMapper;

    private final RedisService redisService;

    public MessageServiceImpl(MessageMapper messageMapper, AccountMapper accountMapper, AccountService accountService, BlacklistMapper blacklistMapper, RedisService redisService) {
        this.messageMapper = messageMapper;
        this.accountMapper = accountMapper;
        this.accountService = accountService;
        this.blacklistMapper = blacklistMapper;
        this.redisService = redisService;
    }

    @Override
    public void add(Long sourceId, Long targetId, String content) {

        //  判断账号是否存在
        if (accountMapper.selectById(sourceId) == null ) {
            throw new RuntimeException("账号不存在");
        }
        if (accountMapper.selectById(targetId) == null ) {
            throw new RuntimeException("账号不存在");
        }

        //  判断是否为黑名单
        QueryWrapper<BlackList> qw = new QueryWrapper<>();
        qw.eq("source_id", sourceId);
        qw.eq("target_id", targetId);
        if (blacklistMapper.selectOne(qw) != null) {
            throw new RuntimeException("无法发送消息");
        }
        qw = new QueryWrapper<>();
        qw.eq("source_id", targetId);
        qw.eq("target_id", sourceId);
        if (blacklistMapper.selectOne(qw) != null) {
            throw new RuntimeException("无法发送消息");
        }

        int i = messageMapper.insert(new Message(0L, sourceId, targetId, content, false, LocalDateTime.now()));
        if (i == 0) {
            throw new RuntimeException("发送失败");
        }

        //  发送成功后，向redis中添加未读消息数据
        Object to = redisService.hGet("msglist:" + targetId, String.valueOf(sourceId));
        Object so = redisService.hGet("msglist:" + sourceId, String.valueOf(targetId));
        if (to == null) {
            to = 0L;
        }
        if (so == null) {
            so = 0L;
        }
        try {
            synchronized (this) {
                long unreadCount = Long.parseLong(String.valueOf(to));
                //  自增+1
                redisService.hSet("msglist:" + targetId, String.valueOf(sourceId), unreadCount + 1);

                //  设置对方数据
                redisService.hSet("msglist:" + sourceId, String.valueOf(targetId), so);
            }
        }
        catch (Exception ignore) {}
    }

    @Override
    public List<MsgListDto> listMsgList(Long accountId) {

        List<MsgListDto> list = new ArrayList<>();

        //  查询redis数据库
        Map<Object, Object> map = redisService.hGetAll("msglist:" + accountId);
        for (Object key : map.keySet()) {
            try {
                long targetAccountId = Long.parseLong(String.valueOf(key));
                long unreadCount = Long.parseLong(String.valueOf(map.get(key)));
                Message lastMessage = getLastMessage(accountId, targetAccountId);
                MsgListDto dto = toMsgListItemDto(lastMessage, unreadCount, accountId);
                list.add(dto);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }

        return list;
    }

    @Override
    public List<Message> getAllMessages(Long accountId) {
        QueryWrapper<Message> qw = new QueryWrapper<>();
        qw.eq("source_id", accountId);
        List<Message> list1 = messageMapper.selectList(qw);
        qw = new QueryWrapper<>();
        qw.eq("target_id", accountId);
        List<Message> list2 = messageMapper.selectList(qw);
        list1.addAll(list2);
        return list1;
    }

    @Override
    public List<Message> getAllMessageByTargetId(Long targetId, Long accountId) {
        QueryWrapper<Message> qw = new QueryWrapper<>();
        qw.eq("source_id", accountId);
        qw.eq("target_id", targetId);
        List<Message> list1 = messageMapper.selectList(qw);
        qw = new QueryWrapper<>();
        qw.eq("source_id", targetId);
        qw.eq("target_id", accountId);
        List<Message> list2 = messageMapper.selectList(qw);
        list1.addAll(list2);
        return list1;
    }

    @Override
    public Message getLastMessage(Long sourceId, Long targetId) {

        QueryWrapper<Message> qw = new QueryWrapper<>();
        qw.eq("source_id", sourceId);
        qw.eq("target_id", targetId);
        qw.or();
        qw.eq("source_id", targetId);
        qw.eq("target_id", sourceId);
        qw.orderByDesc("create_time");
        qw.last("limit 1");
        return messageMapper.selectOne(qw);
    }

    @Override
    public void setRead(Long sourceId, Long targetId) {
        redisService.hSet("msglist:" + sourceId, String.valueOf(targetId), String.valueOf(0L));
    }

    @Override
    public void setReadAll(Long sourceId) {
        Map<Object, Object> map = redisService.hGetAll("msglist:" + sourceId);
        for (Object o : map.keySet()) {
            try {
                redisService.hSet("msglist:" + sourceId, String.valueOf(o), 0L);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public List<ChatMessageDto> listMsgForTarget(Long targetId, Long startId, Long sourceId) {
        QueryWrapper<Message> qw = new QueryWrapper<>();
        qw.eq("source_id", sourceId);
        qw.eq("target_id", targetId);
        qw.lt("id", startId);
        qw.or();
        qw.eq("source_id", targetId);
        qw.eq("target_id", sourceId);
        qw.lt("id", startId);
        qw.orderByDesc("create_time");
        qw.last("limit 20");

        List<Message> list = messageMapper.selectList(qw);
        List<ChatMessageDto> l = new ArrayList<>();
        Object o = 0L;
        try {
            o = redisService.hGet("msglist:" + sourceId, String.valueOf(targetId));
        }
        catch (Exception ignore) {}
        for (Message message : list) {
            l.add(toChatMessageDto(message));
        }
        return l;
    }

    @Override
    public List<ChatMessageDto> listNewMsg(Long targetId, Long endId, Long sourceId) {
        QueryWrapper<Message> qw = new QueryWrapper<>();
        qw.eq("source_id", sourceId);
        qw.eq("target_id", targetId);
        qw.gt("id", endId);
        qw.or();
        qw.eq("source_id", targetId);
        qw.eq("target_id", sourceId);
        qw.gt("id", endId);
        qw.orderByDesc("create_time");

        List<Message> list = messageMapper.selectList(qw);
        List<ChatMessageDto> l = new ArrayList<>();
        Object o = 0L;
        try {
            o = redisService.hGet("msglist:" + sourceId, String.valueOf(targetId));
        }
        catch (Exception ignore) {}
        for (Message message : list) {
            l.add(toChatMessageDto(message));
        }

        return l;
    }

    @Override
    public void deleteMsgListByTarget(Long targetId, Long sourceId) {
        redisService.hDel("msglist:" + sourceId, String.valueOf(targetId));
    }

    private MsgListDto toMsgListItemDto(Message message, Long unreadCount, Long accountId) {

        if (message == null) {
            return null;
        }

        long targetId = message.getTargetId();
        if (accountId.equals(targetId)) {
            targetId = message.getSourceId();
        }

        return MsgListDto
                .builder()
                .id(message.getId())
                .targetId(targetId)
                .targetNickName(accountMapper.getNicknameById(targetId))
                .content(message.getContent())
                .time(message.getCreateTime())
                .unReadCount(unreadCount)
                .build();
    }

    private ChatMessageDto toChatMessageDto(Message message) {

        if (message == null) {
            return null;
        }

        return ChatMessageDto
                .builder()
                .id(message.getId())
                .sourceId(message.getSourceId())
                .targetId(message.getTargetId())
                .content(message.getContent())
                .time(message.getCreateTime())
                .build();
    }
}
